package priv.dajie.tzzq.bot.c5;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import priv.dajie.tzzq.bot.Bot;
import priv.dajie.tzzq.common.ApplicationContextUnit;
import priv.dajie.tzzq.model.QualityName;
import priv.dajie.tzzq.model.SellType;
import priv.dajie.tzzq.service.PlatformService;
import priv.dajie.tzzq.spider.model.ItemInfo;
import priv.dajie.tzzq.bot.exception.*;
import priv.dajie.tzzq.bot.model.*;
import priv.dajie.tzzq.common.Tools;
import priv.dajie.tzzq.verifyRec.VerifyRec;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class C5Bot implements Bot {
    private C5User c5User;

    private PlatformService c5Service  = (PlatformService) ApplicationContextUnit.getInstance().getBean("c5Service");
//    private BuffTable buffTable;
//    private SteamTable steamTable;
    private Map<String, TradeState> stateMap;
    private BigDecimal feeRate;
    private long feeRateUpdateTime;
    private BigDecimal maxFee;
    private Logger logger = LogManager.getLogger(this.getClass());
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    private final Pattern classIDPattern = Pattern.compile("classid=(.*)&");
    private final Pattern instanceIDPattern = Pattern.compile("instanceid=(.*)");


    {
        stateMap = new HashMap<>();
        stateMap.put("交易取消", TradeState.FAIL);
        stateMap.put("交易成功", TradeState.SUCCESS);
    }

    public C5Bot(C5User c5User) {
        if (!(c5User.isLogin() && c5User.isVerify())) {
            throw new NeedLoginVerifyException("必须完成登陆和短信验证");
        }
        this.c5User = c5User;
    }

    public C5User getC5User() {
        return c5User;
    }

    public void setFeeRate() {
        if (System.currentTimeMillis() - feeRateUpdateTime > 21600000) {
            HttpPost httpPost = new HttpPost("https://www.c5game.com/user/sell/quick.html");
            List<NameValuePair> data = new ArrayList<>();
            data.add(new BasicNameValuePair("appid", "570"));
            data.add(new BasicNameValuePair("id[]", "0"));
            try {
                httpPost.setEntity(new UrlEncodedFormEntity(data));
            } catch (UnsupportedEncodingException e) {
                throw new NetworkException(e);
            }
            String jsonStr = c5User.open(httpPost);
            JSONObject json = JSON.parseObject(jsonStr);
            JSONObject body = json.getJSONObject("body");
            BigDecimal feeRate = body.getBigDecimal("percent").divide(BigDecimal.valueOf(100));
            BigDecimal maxFee = body.getBigDecimal("max_fee");
            this.feeRate = feeRate;
            this.maxFee = maxFee;
            this.feeRateUpdateTime = System.currentTimeMillis();
        }
    }


    @Override
    public HistoryPrices getHistoryPrice(String goodID) {
        return getHistoryPrice(goodID, 7);
    }

    @Override
    public HistoryPrices getHistoryPrice(String goodID, int day) {
        HistoryPrices historyPriceList = new HistoryPrices();
        try {
            URIBuilder uriBuilder = new URIBuilder("https://www.c5game.com/api/product/chart.json");
            uriBuilder.setParameter("item_id", goodID);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            String jsonText = c5User.open(httpGet);
            JSONObject json = JSON.parseObject(jsonText);
            if (json.getInteger("status") == 200) {
                JSONArray prices = json.getJSONObject("body").getJSONObject("day").getJSONArray("ydata");
                JSONArray dates = json.getJSONObject("body").getJSONObject("day").getJSONArray("xdata");
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                for (int i = 0; i < prices.size(); i++) {
                    String dateStr = dates.getString(i);
                    try {
                        Date date = dateFormat.parse(dateStr);
                        historyPriceList.add(new HistoryPrice(date, prices.getBigDecimal(i)));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
//                double sum = 0;
//                int r = 1;
//                for (int i = prices.size() - 1; i >= 0; i--) {
//                    sum += prices.getDouble(i);
//                    r++;
//                    if (r >= day)
//                        break;
//                }
//                r = r > prices.size() ? prices.size() : r;
//                return sum / (r);
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return historyPriceList;
    }

    public C5SteamPackItems getSteamPackItems() { // 从c5中获取steam背包信息
        C5SteamPackItems c5SteamPackItems = new C5SteamPackItems();
        HttpGet httpGet = new HttpGet("https://www.c5game.com/steam/inventory/inventory?appid=570");
        String jsonText = c5User.open(httpGet);
        JSONObject json = JSON.parseObject(jsonText);
        JSONArray datas = json.getJSONObject("body").getJSONArray("items");
        for (int i = 0; i < datas.size(); i++) {
            JSONObject data = datas.getJSONObject(i);
            String steamName = data.getString("market_hash_name");
            String assetid = data.getString("asset_id");
            String classid = data.getString("classid");
            String id = data.getString("id");
            String instanceid = data.getString("instanceid");
            String key = data.getString("key");
            c5SteamPackItems.add(new C5SteamPackItem(steamName, id, assetid, classid, instanceid, key));
        }
        return c5SteamPackItems;
    }

    @Override
    public List<SellItem> getSellItemsBySteamName(String steamName) {
        return getSellItems(steamName, NameType.STEAM_NAME);
    }

    @Override
    public List<BuyItem> getBuyItemsBySteamName(String steamName) {
        return getBuyItems(steamName, NameType.STEAM_NAME);
    }

    @Override
    public List<SellItem> getSellItemsByID(String id) {
        return getSellItems(id, NameType.ID);
    }

    public List<BuyItem> getBuyItemsByID(String id) {
        return getBuyItems(id, NameType.ID);
    }


    @Override
    public List<SellItem> getSellItems(String name, NameType nameType) {
        List<SellItem> sellItems = new ArrayList<>();
        String id;
        if (nameType == NameType.STEAM_NAME) {
            ItemInfo itemInfo = c5Service.getInfoBySteamName(name);
            // 找不到steamName返回null
            if (itemInfo == null) {
                return sellItems;
            }
            id = itemInfo.getId();
        } else {
            id = name;
        }
        // 获取 用于获取销售列表的id
        String html = c5User.open(new HttpGet("https://www.c5game.com/dota/item/index.html?item_id=" + id + "&type=S"));
        Document document = Jsoup.parse(html);
        String url = document.select("tbody#sale-body").first().attr("data-url");
//            URIBuilder uriBuilder = new URIBuilder("https://www.c5game.com/api/product/sale.json");
//            uriBuilder.addParameter("id", itemInfo.getId());
//            uriBuilder.addParameter("gem_id", "0");
//            uriBuilder.addParameter("page", "1");
        HttpGet httpGet = new HttpGet("https://www.c5game.com/" + url);
        String jsonText = c5User.open(httpGet);
        JSONObject json = new JSONObject();
        json = JSON.parseObject(jsonText);

        // 获取失败返回null
        if (json.getInteger("status") != 200) {
            return sellItems;
        }
        JSONArray items = json.getJSONObject("body").getJSONArray("items");
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            int selfSell = item.getInteger("is_self_sell");
            // 自动发货
            PayMethod payMethod = PayMethod.OTHER;
            if (selfSell == 0) {
                payMethod = PayMethod.FAST;
            }
            else if (selfSell == 1) {
                payMethod = PayMethod.OTHER;
            }
            sellItems.add(new SellItem(item.getString("id"), new BigDecimal(item.getString("price")), payMethod));




        }
        return sellItems;
    }

    // 获取求购列表
    public List<BuyItem> getBuyItems(String name, NameType nameType) {
        List<BuyItem> buyItems = new ArrayList<>();
        String c5ID;
        if (nameType == NameType.STEAM_NAME) {
            ItemInfo itemInfo = c5Service.getInfoBySteamName(name);
            if (itemInfo == null) {
                return buyItems;
            }
            c5ID = itemInfo.getId();
        } else {
            c5ID = name;
        }
        // 获取求购列表id
        String html = c5User.open(new HttpGet("https://www.c5game.com/dota/" + c5ID + "-P.html"));
        Document document = Jsoup.parse(html);
        Element element = document.selectFirst("tbody[data-tpl=purchase-tpl]");
        String url = element.attr("data-url");
        String jsonText = c5User.open(new HttpGet("https://www.c5game.com" + url));
        JSONObject json = JSON.parseObject(jsonText);
        if (json.getInteger("status") != 200) {
            if (json.getString("message").equals("暂无求购饰品记录。")) {
                return buyItems;
            }
            throw new NetworkException(json.getString("message"));
        }
        JSONArray items = json.getJSONObject("body").getJSONArray("items");
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            String id = item.getString("id");
            BigDecimal price = item.getBigDecimal("price");
            int num = item.getInteger("need_num");
            buyItems.add(new BuyItem(id, price, num));
        }
        return buyItems;
    }


    public void selfSell(C5SteamPackItem item, BigDecimal price) throws SellException {
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/sell/selfOnSale");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("appid", "570"));
        data.add(new BasicNameValuePair("verify_code", ""));
        data.add(new BasicNameValuePair("item_ids[]", item.getId()));
        data.add(new BasicNameValuePair("asset_ids[]", item.getAssetid()));
        data.add(new BasicNameValuePair("class_ids[]", item.getClassid()));
        data.add(new BasicNameValuePair("instance_ids[]", item.getInstanceid()));
        data.add(new BasicNameValuePair("keys[]", item.getKey()));
        // 整数则去除小数点
        String priceStr = price.toString();
        data.add(new BasicNameValuePair("price", priceStr));
        data.add(new BasicNameValuePair("bargain", "N"));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new NetworkException(e);
        }
        String jsonText = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonText);
        if (json.getInteger("status") != 200) {
            throw new SellException("上架失败：" + json.getString("message"));
        }
    }

    public ChangePriceResults changePrice(SellingItem sellingItem) throws SellException {
        List<SellingItem> sellingItems = new ArrayList<>();
        sellingItems.add(sellingItem);
        return changePrice(sellingItems);
    }

    @Override
    public ChangePriceResults changePrice(List<SellingItem> sellingItems) throws SellException {
        return changePrice(sellingItems, "");
    }

    public String getVerifyCode() throws IOException {
        HttpGet httpGet = new HttpGet("https://www.c5game.com/api/sell/captcha.html?refresh=1");
        String jsonStr = c5User.open(httpGet);
        JSONObject json = JSON.parseObject(jsonStr);
        String imgLink = "https://www.c5game.com/" + json.getString("url");
        HttpResponse response = c5User.openPro(new HttpGet(imgLink), false);
        InputStream inputStream = response.getEntity().getContent();
        byte[] bytes = Tools.readInputStream(inputStream);
        inputStream.close();
        String code = VerifyRec.capreg(bytes).getCode();
        return code;
    }

    public ChangePriceResults changePrice(List<SellingItem> sellingItems, String code) throws SellException { // 修改销售价格  2019.4.29待测试（自售与寄售是否都能修改价格）
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/sell/changePrice.json?appid=570");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("verify_code", code));
        for (SellingItem sellingItem : sellingItems) {
            BigDecimal price = sellingItem.getPrice();
            // 整数则去除小数点
            String priceStr = price.toString();
            data.add(new BasicNameValuePair("id[]", sellingItem.getSellID()));
            data.add(new BasicNameValuePair("price[]", priceStr));
        }
        data.add(new BasicNameValuePair("note[]", ""));
        data.add(new BasicNameValuePair("memo[]", ""));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            throw new NetworkException(e);
        }
        String jsonText = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonText);
        int status = json.getInteger("status");
        switch (status) {
            case 200:
                logger.info("价格修改成功");
                break;
            case 1942:
            case 1943:
                logger.warn("需要验证码，进行验证码识别");
                String vCode;
                while (true) {
                    try {
                        vCode = getVerifyCode();
                        break;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return changePrice(sellingItems, vCode);
            default:
                logger.error("价格修改失败：" + json);
                throw new SellException("价格修改失败" + json);
        }
        ChangePriceResults changePriceResults = new ChangePriceResults();
        for (SellingItem sellingItem : sellingItems) {
            changePriceResults.put(sellingItem.getSellID(), true);
        }


        return changePriceResults;
    }

    public List<C5SellingItem> getSelfSellItems() { // 返回的列表不包含steamName
        // https://www.c5game.com/api/user/selfItems?appid=570&page=1 该链接可获得更详细的销售信息
        //"https://www.c5game.com/api/store/newest.json?id="+c5User.getC5ID() 该链接可获取steamName，已在getSellingItemsPro中使用
        List<C5SellingItem> c5SellingItems = new ArrayList<>();
        HttpGet httpGet = new HttpGet("https://www.c5game.com/api/user/selfItems?appid=570&page=1");
        String jsonText = c5User.open(httpGet);
        JSONObject json = JSON.parseObject(jsonText);
        if (json.getInteger("status") != 200) {
            throw new NetworkException("获取销售物品失败：" + json.getString("message"));
        }
        JSONArray items = json.getJSONObject("body").getJSONArray("list");
        for (int i = 0; i < items.size(); i++) {
            JSONObject item = items.getJSONObject(i);
            String id = item.getString("id");
            BigDecimal price = item.getBigDecimal("price");
//            String assetID = item.getString("asset_id");
            String c5ID = item.getString("item_id");
            String steamName = c5Service.getInfoById(c5ID).getSteamName();
            c5SellingItems.add(new C5SellingItem(steamName, id, price));
        }
        return c5SellingItems;
    }


//    public List<C5SellingItem> getSelfSellItemsPro() { // 返回列表包含steamName
//        List<C5SellingItem> c5SellingItems = getSelfSellItems();
//        HttpGet httpGet = new HttpGet("https://www.c5game.com/api/store/newest.json?id=" + c5User.getC5ID()); // 获取steamName
//        String jsonText = c5User.open(httpGet);
//        JSONObject json = JSON.parseObject(jsonText);
//        JSONArray items = json.getJSONArray("body");
//        Map<String, String> names = new HashMap<>(); // 用于存放id对应的steamName
//        for (int i = 0; i < items.size(); i++) {
//            JSONObject item = items.getJSONObject(i);
//            names.put(item.getString("product_item_id"), item.getString("market_hash_name")); // 从网页中获取steamName并存放进Map中
//        }
//        for (C5SellingItem c5SellingItem : c5SellingItems) {
//            String id = c5SellingItem.getId();
//            c5SellingItem.setSteamName(names.get(id));
//        }
//        return c5SellingItems;
//    }

    public List<C5SellingItem> getOnSellItems() {
        List<C5SellingItem> c5SellingItems = new ArrayList<>();
        String html = c5User.open(new HttpGet("https://www.c5game.com/user/sell/index.html?self_sell_type=1"));
        Document document = Jsoup.parse(html);
        Element itemsDIV = document.selectFirst("div.items");
        Elements items = itemsDIV.select("li.sell-li");
        for (Element item : items) {
            String priceStr = item.selectFirst("span.c5-price").text().replace("￥", "");
            BigDecimal price = new BigDecimal(priceStr);
            String c5ID = item.selectFirst("a.name-ellipsis").attr("href").replace("/dota/", "").replace("-S.html", "");
            String steamName = c5Service.getInfoById(c5ID).getSteamName();
            String id = item.selectFirst("input[name=id[]]").attr("value");
            c5SellingItems.add(new C5SellingItem(steamName, id, price));
        }
        return c5SellingItems;
    }

    public void deliver(String buyerID, boolean autoTrack) throws SellException { // 发送发货报价
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/order/selfDeliver");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("buyer_user_id", buyerID));
        data.add(new BasicNameValuePair("appid", "570"));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
            throw new NetworkException(e);
        }
        String jsonText = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonText);
        if (json.getInteger("status") != 200) {
            throw new SellException("发货报价发送失败：" + json.getString("message"));
        }
    }

    public List<String> autoTracker() { // 待测试 2019.4.28  已测试2019.4.28
        List<String> tradeIDs = new ArrayList<>();
        try {
            URIBuilder uriBuilder = new URIBuilder("https://www.c5game.com/api/offer/tracker.json");
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            List<String> botID = new ArrayList<>();
            int error = 0;
            while (true) { // 用于获取机器人ID
                String jsonText = c5User.open(httpGet);
                JSONObject json = JSON.parseObject(jsonText);
                if (json.getInteger("status") == 404) { // 判断交易数据是否获取成功
                    if (error > 3) {
                        throw new TradeException("当前没有交易信息或交易信息获取失败");
                    }
                    error++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                JSONArray body = json.getJSONArray("body");
                for (int i = 0; i < body.size(); i++) {
                    botID.add(body.getJSONObject(i).getString("id"));
                }
                break;
            }
            // 遍历添加机器人ID
            for (String id : botID) {
                uriBuilder.addParameter("id[]", id);
            }
            httpGet = new HttpGet(uriBuilder.build());
            // 检查次数记录
            int checkNum = 0;
            // 判断机器人是否已经发送交易报价
            while (true) {
                System.out.println("第" + ++checkNum + "次获取报价状态");
                int successNum = 0;
                String jsonText = c5User.open(httpGet);
                JSONObject json = JSON.parseObject(jsonText);
                JSONArray body = json.getJSONArray("body");
                for (int i = 0; i < body.size(); i++) {
                    JSONObject item = body.getJSONObject(i);
                    int statusCode = item.getJSONObject("operation").getInteger("status");
                    String status = item.getString("status");
                    if ((statusCode == 1 && "sent".equals(status)) || statusCode == -1) {
                        successNum++;
                    }
                }
                if (successNum == body.size()) {
                    for (int i = 0; i < body.size(); i++) {
                        JSONObject item = body.getJSONObject(i);
                        String url = item.getJSONObject("operation").getJSONObject("operation").getString("url");
                        String id = url.replace("https://steamcommunity.com/tradeoffer/", "");
                        tradeIDs.add(id);
                    }
                    return tradeIDs;
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return tradeIDs;
    }

    public void onSell(PackItem PackItem, BigDecimal price) throws SellException { //待测试 2019.4.28 已测试 2019.5.1
        // 寄售上架
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/sell/onSale");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("appid", "570"));
        data.add(new BasicNameValuePair("verify_code", ""));
        data.add(new BasicNameValuePair("id[]", PackItem.getId()));
        data.add(new BasicNameValuePair("price[]", price + "")); // 价格可能存在BUG，没有小数位时会有.0
        data.add(new BasicNameValuePair("bargain", "N"));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new NetworkException(e);
        }
        String jsonText = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonText).getJSONObject("body");
        if (json.getInteger("wrong_count") > 0) { // 如果错误大于1则判定上架失败
            JSONArray wrongs = json.getJSONArray("wrong_items");
            throw new SellException("c5寄售上架失败：" + wrongs.getJSONObject(0).getString("reason")); // 抛出上架失败的原因
        }
    }

    @Override
    public PackItems getPack() {
        PackItems packItems = new PackItems();
        int page = 1;
        int pageMax = 1;
        do {
            String html = c5User.open(new HttpGet("https://www.c5game.com/user/inventory/withdraw.html?appid=570&page=" + page));
            Document document = Jsoup.parse(html);
            Elements pageMaxDiv = document.select("li.page");
            if(pageMaxDiv != null && !"".equals(pageMaxDiv.toString())){
//                pageMax = 1;

                pageMax = Integer.parseInt(pageMaxDiv.last().selectFirst("a").text());
            }
            Element ul = document.selectFirst("ul.list-item4.clearfix.user-inventory");
            Elements items = ul.select("li");
            for (Element item : items) {
                String c5idURL = item.select("a.name-ellipsis.ft-base").first().attr("href");
                String c5ID = c5idURL.replace("/dota/", "").replace("-S.html", "");
                String steamName = c5Service.getInfoById(c5ID).getSteamName();
                String id = item.selectFirst("input[name=id[]]").attr("value");
                QualityName qualityName = c5Service.getInfoBySteamName(steamName).getQualityName();
                Element exception = item.selectFirst("div.self-mask");
                boolean isException = false;
                if(!exception.hasClass("hide")){
                    isException = true;
                }
                packItems.add(new PackItem(qualityName, steamName, id, isException));
            }
            page++;
        } while (page <= pageMax);
        return packItems;

    }


    public List<C5PackItem> getC5PackItems() { // 获取C5背包中的物品
        List<C5PackItem> c5PackItems = new ArrayList<>();
        String html = c5User.open(new HttpGet("https://www.c5game.com/user/inventory/withdraw.html"));
        Document document = Jsoup.parse(html);
        Element ul = document.selectFirst("ul.list-item4.clearfix.user-inventory");
        Elements items = ul.select("li");
        for (Element item : items) {
            String c5idURL = item.select("a.name-ellipsis.ft-base").first().attr("href");
            String c5ID = c5idURL.replace("/dota/", "").replace("-S.html", "");
            String steamName = c5Service.getInfoById(c5ID).getSteamName();
            String assetID = item.attr("data-assetid");
            String detailURL = item.attr("data-item-detail");

            Matcher classIDMatCher = classIDPattern.matcher(detailURL);
            String classID = null;
            if (classIDMatCher.find()) {
                classID = classIDMatCher.group(1);
            }

            Matcher instanceIDMatcher = instanceIDPattern.matcher(detailURL);
            String instanceID = null;
            if (instanceIDMatcher.find()) {
                instanceID = instanceIDMatcher.group(1);
            }

            String id = item.selectFirst("input[name=id[]]").attr("value");

            c5PackItems.add(new C5PackItem(steamName, id, assetID, classID, instanceID));
        }
        return c5PackItems;
    }

    public List<SellHistoryItem> getSellHistoryItems(int pageMax){
        return getSellHistoryItems(pageMax, -1);
    }

    /**
     * 获取（time-2小时）以后的出售记录
     * @param time
     * @return java.util.List<priv.dajie.bot.model.SellHistoryItem>
     * @author dajie
     * @date 2019/9/19
     */
    public List<SellHistoryItem> getSellHistoryItemsByTime(long time){

        return getSellHistoryItems(0, time);
    }

    private List<SellHistoryItem> getSellHistoryItems(int pageMax, long time) { // 获取出售的记录，该函数为完成，还有未确定的状态
        List<SellHistoryItem> sellHistoryItems = new ArrayList<>();
        int page = 1;
        pageMax = pageMax != 0 ? pageMax : 9999;
        r:while (page <= pageMax) {
            String url = "https://www.c5game.com/user/sell/sellerHistory.html?page=";
            String html = c5User.open(new HttpGet(url + page));
            Document document = Jsoup.parse(html);
            Elements items = document.selectFirst("tbody").select("tr");
            // 判断页面是否还有元素
            if (items.size() == 1 && items.first().text().equals("当前暂无出售记录")) {
                break;
            }
            for (Element item : items) {
                String c5ID = item.selectFirst("a.td-good-img").attr("href").replace("/dota/", "").replace("-S.html", "");
                String steamName = c5Service.getInfoById(c5ID).getSteamName();
                String tradeID = item.selectFirst("p.mt-5.mb-0.ft-12").text();
                String priceStr = item.selectFirst("span.ft-gold").text().replace("￥", "");
                BigDecimal price = new BigDecimal(priceStr);
                String stateStr = item.selectFirst("td.j_TdAction").selectFirst("p").text();
                String timeStr = item.select("span.ft-12").get(1).text();
                Date date = null;
                try {
                    date = dateFormat.parse(timeStr);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

                // 未完成
                TradeState state = stateMap.get(stateStr);
                if(state == TradeState.OTHER){
                    logger.error("发现未知出售状态："+stateStr);
                }

                sellHistoryItems.add(new SellHistoryItem(steamName, tradeID, price, date, state));

                if(date.getTime() < (time - 60*60*2*1000)){
                    break r;
                }

            }
            page++;
        }
        return sellHistoryItems;
    }

    @Override
    public BuyHistoryItems getBuyHistory(int pageMax, String id, long time) {
        BuyHistoryItems buyHistoryItems = new BuyHistoryItems();
        int page = 1;
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        Date now = new Date();
        int error = 0;
        r:
        while (page <= pageMax) {
            String html;

            try {
                html = c5User.open(new HttpGet("https://www.c5game.com/user/order/buyerHistory.html?page=" + page));
            } catch (NetworkException e) {
                logger.error(e);
                try {
                    Thread.sleep(1000 * ++error);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                continue;
            }
            Document document = Jsoup.parse(html);
            Element tableb = document.selectFirst("table.gx-table-base");
            if (tableb == null) {
                continue;
            }
            Element table = tableb.selectFirst("tbody");
            Elements items = table.select("tr");
            for (Element item : items) {
                Element goodsHerfItem = item.selectFirst("a[href^=/dota]");
                if (goodsHerfItem == null) {
                    break r;
                }
                String goodsHerf = goodsHerfItem.attr("href");
                String goodsID = goodsHerf.replace("/dota/", "").replace("-S.html", "");
                String name = item.selectFirst("span.ft-color.ft-14.name.name-ellipsis-90").text();
                QualityName qualityName = Tools.getQualityName(name);
                String buyID = item.selectFirst("p.mt-5.mb-0.ft-12").text();
                String stateStr = item.selectFirst("td.j_TdAction").selectFirst("p").text();
                TradeState tradeState = Tools.c5BuyStateParse(stateStr);
                String dateStr = item.select("span.ft-12").get(2).text();
                String takeBackId;
                if (tradeState == TradeState.TAKE_BACK) {
                    takeBackId = item.selectFirst("button.j_Receive").attr("data-id");
                }
                else {
                    takeBackId = "";
                }
                Date date = null;
                try {
                    date = simpleDateFormat.parse(dateStr);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                if (time != -1 && date != null && now.getTime() - date.getTime() > time && page > 25) {
                    break r;
                }
                if (tradeState != TradeState.TAKE_BACK) {
                    buyHistoryItems.add(new BuyHistoryItem(qualityName, goodsID, buyID, tradeState, date));
                }
                else {
                    buyHistoryItems.add(new BuyHistoryItem(qualityName, goodsID, buyID, tradeState, date, takeBackId));
                }
                if (id != null && id.equals(buyID)) {
                    break r;
                }
            }
            page++;
        }
        return buyHistoryItems;
    }

    @Override
    public BuyHistoryItems getBuyHistory(int pageMax) {
        return getBuyHistory(pageMax, null, -1);
    }

    @Override
    public BuyHistoryItems getBuyHistoryByTime(long time) {
        return getBuyHistory(200, null, time);
    }

    public BuyHistoryItems getBuyHistory(String id) {
        return getBuyHistory(200, id, -1);
    }

    public String deposit(C5SteamPackItem c5SteamPackItem) {
        List<C5SteamPackItem> ls = new ArrayList<>();
        return deposit(ls);
    }
    public String[] autoDeposit(C5SteamPackItem c5SteamPackItem){
        String trackerId = deposit(c5SteamPackItem);
        return autoTracker(trackerId);
    }

    public String[] autoDeposit(List<C5SteamPackItem> c5SteamPackItems){
        String trackerId = deposit(c5SteamPackItems);
        return autoTracker(trackerId);
    }

    public String deposit(List<C5SteamPackItem> c5SteamPackItems) throws TradeException { // 商品存入C5
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/inventory/deposit.json");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("appid", "570"));
        for (C5SteamPackItem c5SteamPackItem : c5SteamPackItems) {
            data.add(new BasicNameValuePair("id[]", c5SteamPackItem.getId()));
            data.add(new BasicNameValuePair("asset_id[]", c5SteamPackItem.getAssetid()));
            data.add(new BasicNameValuePair("classid[]", c5SteamPackItem.getClassid()));
            data.add(new BasicNameValuePair("instanceid[]", c5SteamPackItem.getInstanceid()));
            data.add(new BasicNameValuePair("key[]", c5SteamPackItem.getKey()));
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            throw new NetworkException(e);
        }
        String jsonStr = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonStr);
        if (json.getInteger("status") != 200) {
            throw new TradeException(json.getString("message"));
        }
//        JSONObject body = json.getJSONObject("body");
        JSONObject offer = json.getJSONObject("offer");
        return offer.getString("id");
    }



    public BigDecimal fee(BigDecimal price) {
        setFeeRate();
        BigDecimal fee;
        if (price.compareTo(new BigDecimal("0.01")) <= 0) {
            fee = new BigDecimal(0);
        } else if (price.compareTo(new BigDecimal("1")) < 0) {
            fee = new BigDecimal("0.01");
        } else {
            fee = price.multiply(feeRate);
            // 保留两位小数（不进行四舍五入）
            fee = fee.setScale(2, BigDecimal.ROUND_DOWN);
            if (fee.compareTo(maxFee)>0) {
                fee = maxFee;
            }
        }
        return fee;

    }

    public BuyResult buyPro(SellItem sellItem, String goodsID) throws BuyException {
        // 购买商品并获取购买状态
        BuyState buyState = buy(sellItem);
        // 如果购买成功则去历史购买中查找交易ID
        if (buyState == BuyState.WAIT || buyState == BuyState.SUCCESS || buyState == BuyState.TAKE_BACK) {
            int error = 0;
            while (true) {
                List<BuyHistoryItem> buyHistoryItems = getBuyHistory(1);
                if (buyHistoryItems.size()==0 && ++error<3) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                for (BuyHistoryItem buyHistoryItem : buyHistoryItems) {
                    if (buyHistoryItem.getGoodsID().equals(goodsID)) {
                        if (buyHistoryItem.getTradeState() == TradeState.TAKE_BACK) {
                            return new BuyResult(buyState, buyHistoryItem.getBuyID(), buyHistoryItem.getTakeBackId());
                        } else {
                            return new BuyResult(buyState, buyHistoryItem.getBuyID());
                        }
                    }
                }
                break;
            }
        }
        else {
            return new BuyResult(buyState, null);
        }

        throw new BuyException("查询购买记录失败");
//        return null;
    }

    @Override
    public BuyState buy(SellItem sellItem) {
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/order/payment.json");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("id", sellItem.getId()));
        data.add(new BasicNameValuePair("paypwd", c5User.getTradePassword()));
        data.add(new BasicNameValuePair("is_nopass", "off"));
        data.add(new BasicNameValuePair("price", sellItem.getPrice() + ""));
        int method = 4;
        // 用于判断购买方式
//        if(sellItem.getPayMethod() == PayMethod.OTHER)
//            method = 3; // 到c5
//        else
//            method = 1; // 到steam
        data.add(new BasicNameValuePair("method", method+""));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String jsonStr;
        try {
            jsonStr = c5User.open(httpPost);
        } catch (NetworkException e) {
            logger.error(e);
            return BuyState.NETWORK_EXC;
        }
        JSONObject json = JSON.parseObject(jsonStr);
        if (json == null) {
            logger.error("返回数据为空：" + json);
            return BuyState.OTHER;
        }
        int stateCode = json.getInteger("status");
        switch (stateCode) {
            case 200:
                if(method==1 && json.getJSONObject("body").getInteger("is_self_sell") == 0)
                    return BuyState.TAKE_BACK;
            case 201:
                return BuyState.WAIT;
            case 404:
                return BuyState.NOT_FOUND;
            case 405:
                return BuyState.CHANGE;
            default:
                logger.error(json);
                return BuyState.OTHER;
        }
    }

    @Override
    public String[] takeBack(List<PackItem> packItems){
        if (packItems.size() > 5) {
            throw new TakeBackException("取回数量大于5");
        }

        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/inventory/withdarw.json");
        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("appid", "570"));
        for (PackItem packItem : packItems) {
            data.add(new BasicNameValuePair("id[]", packItem.getId()));
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String jsonStr = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonStr);
        if (json.getInteger("status") != 200) {
            throw new TakeBackException(json.toString());
        }
        JSONArray wrongs = json.getJSONObject("body").getJSONArray("wrong_items");
        if (wrongs.size() > 0) {
            logger.warn("c5背包存在异常饰品：" + json);
        }
        if (json.getJSONObject("body").get("offer") instanceof String) {
            logger.warn("存在异常品品" + json);
            return new String[0];
        }
        JSONArray jsonArray = json.getJSONObject("body").getJSONArray("offer");
        String[] ids = new String[jsonArray.size()];
        for (int i = 0; i < jsonArray.size(); i++) {
            ids[i] = jsonArray.getJSONObject(i).getString("id");
        }
        logger.info("发送取回请求：" + json);
        return ids;
    }

    @Override
    public String[] autoTakeBack(List<PackItem> packItems) {
        String[] ids = takeBack(packItems);
        return autoTracker(ids);
    }

    @Override
    public SellingMap getSelfSell() {
        return getSell(SellType.SELF_SELL);
    }

    @Override
    public SellingMap getOnSell() {
        return getSell(SellType.ON_SELL);
    }

    private SellingMap getSell(SellType sellType) {
        SellingMap sellingMap = new SellingMap();
        String html = c5User.open(new HttpGet("https://www.c5game.com/user/sell/index.html?self_sell_type=" + sellType.getC5Num()));
        Document document = Jsoup.parse(html);
        Element itemsDIV = document.selectFirst("div.items");
        Elements items = itemsDIV.select("li.sell-li");
        for (Element item : items) {
            String priceStr = item.selectFirst("span.c5-price").text().replace("￥", "");
            BigDecimal price = new BigDecimal(priceStr);
            String c5ID = item.selectFirst("a.name-ellipsis").attr("href").replace("/dota/", "").replace("-S.html", "");
            String id = item.selectFirst("input[name=id[]]").attr("value");
            sellingMap.put(id, new SellingItem(c5ID, id, price));
        }
        return sellingMap;
    }

    // 获取销售中商品的最低价
    public MinPrices getMinPrices(List<SellingItem> c5SellingItems) {
        String url = "https://www.c5game.com/api/sell/changePriceQuick";
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            uriBuilder.setParameter("self_sell_type", "1");
            uriBuilder.setParameter("appid", "570");
            for (SellingItem c5SellingItem : c5SellingItems) {
                uriBuilder.addParameter("id[]", c5SellingItem.getSellID());
            }
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            String jsonStr = c5User.open(httpGet);
            JSONObject json = JSON.parseObject(jsonStr);
            if (json.getInteger("status") != 200) {
                throw new DataException("获取最低价失败：" + json);
            }
            JSONArray items = json.getJSONObject("body").getJSONArray("items");
            MinPrices minPrices = new MinPrices();
            for (int i = 0; i < items.size(); i++) {
                JSONObject item = items.getJSONObject(i);
                String id = item.getString("id");
                BigDecimal price = item.getBigDecimal("money_min_sell");
                minPrices.put(id, price);
            }
            return minPrices;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

    public TrackStateInfo tracker(String id){
        String[] ids = new String[1];
        ids[0] = id;
        return tracker(ids)[0];
    }

    /**
     * @Description 跟踪单号的当前状态 待测试
     * @Param [IDs]
     * @Return void
     * @Author dajie
     * @Date 2019/9/16
     **/
    @Override
    public TrackStateInfo[] tracker(String[] IDs) {
        HttpGet httpGet = createTrackerUrl(IDs);
        String jsonStr = c5User.open(httpGet);
        JSONObject json = JSON.parseObject(jsonStr);
        if(json.getInteger("status") == 404){
            return new TrackStateInfo[0];
        }
        else if (json.getInteger("status") != 200) {
            throw new DataException("跟踪报价时返回数据异常：" + json);
        }
        JSONArray datas = json.getJSONArray("body");
        TrackStateInfo[] trackStateInfos = new TrackStateInfo[datas.size()];
        for(int i=0;i<datas.size();i++){
            JSONObject data = datas.getJSONObject(i);
            String id = data.getString("id");
            int status = data.getInteger("trade_offer_status");
            TrackState trackState;
            String tradeId = null;
            switch (status){
                case 0:
                case 9:
                    trackState = TrackState.SENDING;
                    break;
                case 2:
                    trackState = TrackState.SENDED;
                    String url = datas.getJSONObject(i).getJSONObject("operation").getJSONObject("operation").getString("url");
                    tradeId = url.replace("https://steamcommunity.com/tradeoffer/", "");
                    break;
                case 3:
                    trackState = TrackState.SUCCESS;
                    break;
                case 6:
                case 7:
                    trackState = TrackState.FAIL;
                    break;
                default:
                    trackState = TrackState.OTHER;
                    logger.error("未知状态"+status+"："+ data.getJSONObject("operation").getString("message"));
            }
            trackStateInfos[i] = new TrackStateInfo(id, trackState, tradeId);

        }
        return trackStateInfos;
    }

    private HttpGet createTrackerUrl(String[] IDs) {
        URIBuilder uriBuilder = null;
        try {
            uriBuilder = new URIBuilder("https://www.c5game.com/api/offer/tracker.json");
            uriBuilder.setParameter("from", "inventory");
            uriBuilder.setParameter("is_accept_offer_by_steam_client", "0");
            for (String id : IDs) {
                uriBuilder.addParameter("id[]", id);
            }
            return new HttpGet(uriBuilder.build());
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        // 基本不会触发该异常
        throw new DataException("跟踪链接生成失败：" + uriBuilder);
    }

    public String[] autoTracker(String id) {
        String[] ids = new String[1];
        ids[0] = id;
        return autoTracker(ids);
    }

    /**
     * @Description 自动跟踪单号并返回steam交易id
     * @Param [IDs 需要跟踪的id]
     * @Return java.lang.String[] - steam交易单号
     * @Author dajie
     * @Date 2019/9/16
     **/
    public String[] autoTracker(String[] IDs) {

        if (IDs.length == 0) {
            return new String[0];
        }
        HttpGet httpGet = createTrackerUrl(IDs);
        int count = 0;
        int error = 0;
        while (true) {
            logger.info("等待c5机器人处理：第" + ++count + "次");
            String jsonStr;
            try {
                jsonStr = c5User.open(httpGet);
                error = 0;
            } catch (NetworkException e) {
                logger.error(e);
                try {
                    Thread.sleep(1000 * error++);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                if (error > 10) {
                    throw e;
                }
                continue;
            }

            JSONObject json = JSON.parseObject(jsonStr);
            JSONArray datas = json.getJSONArray("body");
            int complete = 0;
//                System.out.println(jsonStr);
            int errorItem = 0;
            for (int i = 0; i < datas.size(); i++) {
                if (datas.getJSONObject(i).getJSONObject("operation").getInteger("status") == 1 && datas.getJSONObject(i).getString("status").equals("sent")) {
                    complete++;
                }
                if (datas.getJSONObject(i).getJSONObject("operation").getInteger("status") == -1) {
                    complete++;
                    errorItem++;
                }
            }
            if (complete == datas.size()) {
                String[] steamTradeItems = new String[datas.size() - errorItem];
                for (int i = 0, j = 0; i < datas.size(); i++) {
                    String url = datas.getJSONObject(i).getJSONObject("operation").getJSONObject("operation").getString("url");
                    if ("/".equals(url)) {
                        logger.warn("饰品取回异常：" + datas.getJSONObject(i).getJSONObject("operation").getJSONObject("operation"));
                        continue;
                    }
                    steamTradeItems[j++] = url.replace("https://steamcommunity.com/tradeoffer/", "");
                }
                logger.info("报价已发送：" + json);
                return steamTradeItems;
            }
            if (count > 150) {
                return new String[0];
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }



    public String autoReceiveOffer(BuyHistoryItem buyHistoryItem){
        String[] ids =  autoTracker(receiveOffer(buyHistoryItem));
        return ids[0];
    }

    public String autoReceiveOffer(String takeBackId){
        return autoTracker(receiveOffer(takeBackId))[0];
    }

    public String receiveOffer(BuyHistoryItem buyHistoryItem){
        if (buyHistoryItem.getTradeState() != TradeState.TAKE_BACK){
            throw new DataException("商品不是待取回状态");
        }
        return receiveOffer(buyHistoryItem.getTakeBackId());

    }

    public String receiveOffer(String takeBackId){
        HttpPost httpPost = new HttpPost("https://www.c5game.com/api/order/create-receive-offer");

        List<NameValuePair> data = new ArrayList<>();
        data.add(new BasicNameValuePair("id", takeBackId));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(data));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String jsonStr = c5User.open(httpPost);
        JSONObject json = JSON.parseObject(jsonStr);
        if(json.getInteger("status")!=200){
            throw new DataException("取回商品时，返回数据异常："+json);
        }
        HttpGet httpGet = new HttpGet("https://www.c5game.com/api/offer/tracker.json?from=buyer_history");
        jsonStr = c5User.open(httpGet);
        json = JSON.parseObject(jsonStr);
        JSONArray datas = json.getJSONArray("body");
        return datas.getJSONObject(0).getString("id");
    }

    @Override
    public boolean isTrading() {
        HttpGet httpGet = new HttpGet("https://www.c5game.com/api/offer/tracker.json?from=inventory");
        String jsonStr;
        try {
            jsonStr = c5User.open(httpGet);
        } catch (NetworkException e) {
            logger.error(e);
            return true;
        }
        JSONObject json = JSON.parseObject(jsonStr);
        return json.getInteger("status") != 404;
    }



}
